﻿using System;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Numerics;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using CSparse;
using CSparse.IO;
using TestApp.Test;

namespace TestApp
{
    public partial class Form1 : Form
    {
        IConsoleWriter writer;

        string folder = Application.StartupPath;
        Result result;

        MemoryInfo memInfo;
        FormSpy formSpy;

        public Form1()
        {
            InitializeComponent();

            Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            writer = new RichTextBoxWriter(this.tbLog);

            cbSolver.SelectedIndex = 0;
            cbOrder.SelectedIndex = 1;

            writer.WriteLine("CSparse.NET - A Concise Sparse Matrix Library for .NET (v3.1.2)");
            writer.WriteLine(Color.DimGray, "Press Ctrl-'Load' to open examples window.");

            Task.Factory.StartNew(() =>
            {
                memInfo = new MemoryInfo(writer);
            });
        }

        #region Task callbacks

        private void LoadCallback(Result result)
        {
            Action a = () =>
            {
                if (!result.Success)
                {
                    LogError(result.Message);
                }
                else
                {
                    btnAnalyse.Enabled = true;
                    btnSpy.Enabled = true;
                    btnSolve.Enabled = false;

                    this.result = result;
                }
            };

            this.Invoke(a);
        }

        private void AnalyseCallback(Result result)
        {
            Action a = () =>
            {
                if (!result.Success)
                {
                    LogError(result.Message);
                }
                else
                {
                    btnAnalyse.Enabled = false;
                    btnSolve.Enabled = true;
                }
            };

            this.Invoke(a);
        }

        private void SolveCallback(Result result)
        {
            Action a = () =>
            {
                memInfo.StopReporting();
                btnSolve.Enabled = true;

                if (!result.Success)
                {
                    LogError(result.Message);
                }
                else
                {
                    tbLog.AppendText(result.Message + "\r\n");
                }

                // Free memory allocated during factorization.
                GC.Collect();
            };

            this.Invoke(a);
        }

        #endregion

        private void btnLoad_Click(object sender, EventArgs e)
        {
            if (ModifierKeys == Keys.Control)
            {
                var form = new FormExamples();

                if (form.ShowDialog() == DialogResult.OK)
                {
                    string file = form.FileName;
                    folder = Path.GetDirectoryName(file);
                    LoadMatrix(file);
                }
            }
            else
            {
                OpenFileDialog ofd = new OpenFileDialog();

                ofd.Filter = "MatrixMarket format (*.mtx)|*.mtx";
                ofd.Filter += "|All files (*.*)|*.*";
                ofd.InitialDirectory = folder;
                ofd.FileName = "";

                if (ofd.ShowDialog() == DialogResult.OK)
                {
                    string file = ofd.FileName;
                    folder = Path.GetDirectoryName(file);
                    LoadMatrix(file);
                }
            }
        }

        private void btnAnalyse_Click(object sender, EventArgs e)
        {
            if (result.IsComplex())
            {
                TestRunner.Analyse(result.GetProblemComplex(), AnalyseCallback);
            }
            else
            {
                TestRunner.Analyse(result.GetProblemDouble(), AnalyseCallback);
            }
        }

        private void btnSolve_Click(object sender, EventArgs e)
        {
            string message = String.Empty;

            var solver = cbSolver.SelectedItem.ToString();
            var order = GetOrdering(cbOrder.SelectedIndex);

            if (result.IsComplex())
            {
                var problem = result.GetProblemComplex();

                if (!TestRunner.CheckSelectedSolver(problem, solver, order, ref message))
                {
                    LogError(message);
                    return;
                }

                btnSolve.Enabled = false;
                tbLog.AppendText("Solving system ...\r\n");
                TestRunner.Solve(problem, solver, order, SolveCallback);
            }
            else
            {
                var problem = result.GetProblemDouble();

                if (!TestRunner.CheckSelectedSolver(problem, solver, order, ref message))
                {
                    LogError(message);
                    return;
                }

                btnSolve.Enabled = false;
                tbLog.AppendText("Solving system ...\r\n");
                TestRunner.Solve(problem, solver, order, SolveCallback);
            }

            memInfo.StartReporting();
        }

        private void btnSpy_Click(object sender, EventArgs e)
        {
            if (formSpy == null)
            {
                formSpy = new FormSpy();
            }

            if (formSpy != null && !formSpy.IsDisposed)
            {
                if (!formSpy.Visible)
                {
                    formSpy.Show(this);
                }

                if (result.IsComplex())
                {
                    formSpy.Spy(result.GetProblemComplex().A);
                }
                else
                {
                    formSpy.Spy(result.GetProblemDouble().A);
                }
            }
        }

        private void tbLog_LinkClicked(object sender, LinkClickedEventArgs e)
        {
            try
            {
                System.Diagnostics.Process.Start(e.LinkText);
            }
            catch (Exception)
            {
            }
        }

        private void LoadMatrix(string file)
        {
            if (String.IsNullOrEmpty(file) || !File.Exists(file))
            {
                LogError("Invalid input file.");
                return;
            }

            tbLog.AppendText("\r\nLoading matrix '" + Path.GetFileName(file) + "'\r\n");

            if (MatrixMarketReader<double>.IsComplex(file))
            {
                // No LDL' factorization for complex matrices.
                if (cbSolver.Items.Count == 4)
                {
                    cbSolver.Items.RemoveAt(3);
                }

                Problem<Complex>.Load(file, writer, LoadCallback);
            }
            else
            {
                // For real matrices add LDL' factorization.
                if (cbSolver.Items.Count < 4)
                {
                    cbSolver.Items.Add("LDL'");
                }

                Problem<double>.Load(file, writer, LoadCallback);
            }
        }

        private void LogError(string message)
        {
            var saveColor = tbLog.ForeColor;
            tbLog.SelectionColor = Color.Peru;
            tbLog.AppendText(message + "\r\n");
            tbLog.SelectionColor = saveColor;
        }

        private ColumnOrdering GetOrdering(int k)
        {
            switch (k)
            {
                case 1:
                    return ColumnOrdering.MinimumDegreeAtPlusA;
                case 2:
                    return ColumnOrdering.MinimumDegreeStS;
                case 3:
                    return ColumnOrdering.MinimumDegreeAtA;
            }
            return ColumnOrdering.Natural;
        }
    }
}
